package statistics;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;

import org.jfree.data.xy.XYSeries;

public class SimulationStatistics {

	BufferedReader in = null;
//	BufferedWriter out = null;

	class Node {
		public long id;
		public long clock;
		public long skew;

		public long virtualLocalReference;
	}

	Vector<Node> nodes = null;
	
	public long maxGlobal = 0;
	public long maxLocal = 0;
	
	public long maxAvgGlobal = 0;
	public long maxAvgLocal = 0;
	
	long start = 0;
	long stop = Long.MAX_VALUE;

	public SimulationStatistics(String inputFile, String outputFile,long start, long stop) {

//		createFile(outputFile);

		try {
			this.start = start;
			this.stop  = stop;
			readFile(inputFile);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	private void readFile(String file) throws FileNotFoundException {
		in = new BufferedReader(new FileReader(new File(file)));

		String line = null;

		long second = 0;

		try {
			while ((line = in.readLine()) != null) {

				StringTokenizer tokenizer = new StringTokenizer(line, " :[]=x");

				while (tokenizer.hasMoreElements()) {
					Node node = new Node();

					String s = tokenizer.nextToken();
					second = Long.valueOf(s, 10);

					s = tokenizer.nextToken();
					node.id = Long.valueOf(s, 10);

					s = tokenizer.nextToken();
					node.clock = Long.valueOf(s, 10);

					s = tokenizer.nextToken();
					node.skew = Long.valueOf(s, 10);

					if (node.id == 1) {

						if (nodes == null) {
							nodes = new Vector<Node>();
						} else {
							calculateStatistics(second);
							nodes = new Vector<Node>();
						}
					}

					nodes.add(node);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
//		close();
	}

	private void calculateStatistics(long second) {	
		if((second > start) && (second < stop)){
			long global = calculateAverageGlobalSkew();
			long local = calculateAverageLocalSkew();
			
			if(local > maxAvgLocal)
				maxAvgLocal = local;
			
			if(global > maxAvgGlobal)
				maxAvgGlobal = global;		
			
			global = calculateGlobalSkew();
			local = calculateLocalSkew();
			
			if(local > maxLocal)
				maxLocal = local;
			
			if(global > maxGlobal)
				maxGlobal = global;	
		}
		
//		String s="";
//		s += second + " " + global + " " + local;
//		
//		write(s);
	}

	private long calculateLocalSkew() {
		
		long localSkew = 0;
		int size =  nodes.size();
		
		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			int position = nodes.indexOf(node);
			long skew = 0;

			if (position > 0) {
				Node prevNeighbor = nodes.elementAt(position - 1);
				skew = prevNeighbor.clock - node.clock;
				if(Math.abs(skew)>Math.abs(localSkew))
					localSkew = skew;
			}

			if (position < (size -1)) {
				Node nextNeighbor = nodes.elementAt(position + 1);
				skew = nextNeighbor.clock - node.clock;
				
				if(Math.abs(skew)>Math.abs(localSkew))
					localSkew = skew;
			}
		}

		return Math.abs(localSkew);
	}

	private long calculateGlobalSkew() {
		
		long global= 0;
		
		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			
			for (Iterator<Node> iterator2 = nodes.iterator(); iterator2.hasNext();) {
				Node node2 = (Node) iterator2.next();
				
				if(node != node2){
					long diff = node.clock - node2.clock;
					if(Math.abs(diff)==7276)
						diff++;
					if(Math.abs(diff)>Math.abs(global))
						global = diff;
				}
			}
			

		}
		return Math.abs(global);
	}

	private long calculateAverageLocalSkew() {
		calculateVirtualLocalReferences();
		int N = nodes.size();
		
		long sum = 0;
		
		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			long diff = (node.clock - node.virtualLocalReference);
			sum += diff*diff;
		}				
		

		return (long) Math.sqrt(sum/N);
	}

	private long calculateAverageGlobalSkew() {
		long virtualReference = calculateVirtualGlobalReference();
		int N = nodes.size();
		
		long sum = 0;
		
		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			long diff = (node.clock - virtualReference);
			sum += diff*diff;
		}				

		return (long) Math.sqrt(sum/N);
	}

	private long calculateVirtualGlobalReference() {

		int N = nodes.size();

		Node ref = nodes.elementAt(0);
		long mean = 0;
		long meanRest = 0;

		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			long diff = node.clock - ref.clock;

			mean += diff / N;
			meanRest += diff % N;
		}

		mean += ref.clock + meanRest / N;

		return mean;
	}

	private void calculateVirtualLocalReferences() {

		int size = nodes.size();
		
		for (Iterator<Node> iterator = nodes.iterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			int position = nodes.indexOf(node);
			long mean = 0;
			int N = 1;

			if (position > 0) {
				Node prevNeighbor = nodes.elementAt(position - 1);
				mean += prevNeighbor.clock - node.clock;
				N++;
			}

			if (position < (size -1)) {
				Node nextNeighbor = nodes.elementAt(position + 1);
				mean += nextNeighbor.clock - node.clock;
				N++;
			}
			
			node.virtualLocalReference = node.clock + mean/N;
		}
	}

//	private void createFile(String file) {
//		try {
//			out = new BufferedWriter(new FileWriter(new File(file)));
//		} catch (IOException e1) {
//			e1.printStackTrace();
//		}
//	}

//	public void write(String data) {
//		try {
//			out.append(data);
//			out.newLine();
//			out.flush();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
//
//	public void close() {
//		try {
//			out.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
	
	public static void main(String[] args) {
		SimulationGraph localGraph = new SimulationGraph();
		SimulationGraph globalGraph = new SimulationGraph();
		XYSeries[] series;
		
		series = processSimulationResult("../jwsnsim/application.appFcsa.FloodingNode","FCSA",190000,200000);
		globalGraph.dataset.addSeries(series[0]);
		localGraph.dataset.addSeries(series[1]);
		
//		series = processSimulationResult("../jwsnsim/application.appFtsp.FtspNode","FTSP",490000,500000);
//		globalGraph.dataset.addSeries(series[0]);
//		localGraph.dataset.addSeries(series[1]);

		series = processSimulationResult("../jwsnsim/application.appPulseSync.PulseSyncNode","PulseSync",190000,200000);
		globalGraph.dataset.addSeries(series[0]);
		localGraph.dataset.addSeries(series[1]);
				
		series = processSimulationResult("../jwsnsim/application.appSelfFlooding.SelfFloodingNode","AVTS",190000,200000);
		globalGraph.dataset.addSeries(series[0]);
		localGraph.dataset.addSeries(series[1]);
		
//		series = processSimulationResult("../jwsnsim/application.appPI.PINode","AvgPISync");
//		globalGraph.dataset.addSeries(series[0]);
//		localGraph.dataset.addSeries(series[1]);
		
		localGraph.show();
		globalGraph.show();			
	}

	private static XYSeries[] processSimulationResult(String root,String protocolName,int intervalStart,int intervalEnd) {
		
		XYSeries[] series = new XYSeries[4];
		series[0] = new XYSeries(protocolName+" Global");
		series[1] = new XYSeries(protocolName+" Local");
		series[2] = new XYSeries(protocolName+" Avg. Global");
		series[3] = new XYSeries(protocolName+" Avg. Local");
		
		SimulationStatistics s;		
				
		System.out.println("---------------------------------------------------");
		System.out.println(root);
		
		for(int i = 10;i<=100;i+=10){
			long averages[] = new long[4];
			
			long global[] = new long[5];
			long local[] = new long[5];
			long avgGlobal[] = new long[5];
			long avgLocal[] = new long[5];
			
			for(int j= 1;j<=5;j++){
				String file = root + "_diameter:"+i+"_count:"+j+".txt";
				s = new SimulationStatistics(file,file,intervalStart,intervalEnd);
				
				global[j-1] = s.maxGlobal;
				local[j-1] = s.maxLocal;
				avgGlobal[j-1] = s.maxAvgGlobal;
				avgLocal[j-1] = s.maxAvgLocal;
				
				System.out.println(" Global:"+s.maxGlobal+ " Local:"+s.maxLocal+ " AvgGlobal:"+s.maxAvgGlobal+" AvgLocal:"+s.maxAvgLocal);
			}
			
			Arrays.sort(global);
			Arrays.sort(local);
			Arrays.sort(avgGlobal);
			Arrays.sort(avgLocal);
			
			for (int j = 0; j < 5; j++) {
				averages[0] += global[j];
				averages[1] += local[j];
				averages[2] += avgGlobal[j];
				averages[3] += avgLocal[j];
			}
						
			series[0].add(i,averages[0]/5);
			series[1].add(i,averages[1]/5);
			series[2].add(i,averages[2]/5);
			series[3].add(i,averages[3]/5);
			
			System.out.println("Diameter:"+i+" Global:"+averages[0]/5+ " Local:"+averages[1]/5+ " AvgGlobal:"+averages[2]/5+" AvgLocal:"+averages[3]/5);
		}
		System.out.println("---------------------------------------------------");
		
		return series;
	}
}
